from tornado.web import RequestHandler, url, authenticated

from bopress import options, metas, user
from bopress.cache import Cache
from bopress.forms import FormData, UserRegistryFrom, UserLoginFrom
from bopress.hook import do_action, apply_filters, AdminMenus
from bopress.log import Logger
from bopress.mail import Mailer
from bopress.model import Users
from bopress.orm import SessionFactory
from bopress.settings import SESSION_TIMEOUT
from bopress.utils import Utils


class BaseHandler(RequestHandler):
    def head_json(self):
        self.set_header("Content-Type", "text/json;charset=utf-8")

    def get_template_namespace(self):
        """Returns a dictionary to be used as the default template namespace.

        May be overridden by subclasses to add or modify values.

        The results of this method will be combined with additional
        defaults in the `tornado.template` module and keyword arguments
        to `render` or `render_string`.
        """
        namespace = dict(
            handler=self,
            request=self.request,
            current_user=self.current_user,
            locale=self.locale,
            _=self.locale.translate,
            pgettext=self.locale.pgettext,
            static_url=self.static_url,
            xsrf_form_html=self.xsrf_form_html,
            reverse_url=self.reverse_url,
            # Custom
            nickname=self.get_current_user_nickname,
            auth=self.auth,
            session=self.session,
            do_action=do_action,
            apply_filters=apply_filters,
            api=self.api_url,
            admin_url=self.admin_url
        )
        namespace.update(self.ui)
        return namespace

    def api_url(self, *args):
        return super(BaseHandler, self).reverse_url("api", *args)

    def admin_url(self, *args):
        return super(BaseHandler, self).reverse_url("admin", *args)

    def render_json(self, data=None, msg="", success=True):
        t = dict()
        t["message"] = msg
        t["success"] = success
        if data:
            t["data"] = data
        else:
            t["data"] = ""
        self.head_json()
        self.write(Utils.encode(t))

    def render_datatables(self, dataset=None, records_total=0, records_filtered=0):
        """
        为JQuery DataTables 返回JSON数据
        :param dataset: 表数据
        :param records_total: 总记录数
        :param records_filtered: 总过滤记录数
        """
        if not dataset:
            dataset = list()
        r = dict()
        r["draw"] = self.get_argument("draw", 1)
        r["recordsTotal"] = records_total
        r["recordsFiltered"] = records_filtered
        r["data"] = dataset
        self.head_json()
        self.write(Utils.encode(r))

    # def render_html(self, file_name, *args, **kwargs):
    #     self.application.settings.get("template_path")

    def auth(self, caps=set(), is_redirect=False, is_api=True):
        """
        Http 权限认证
        :param caps: 权限集合
        :param is_redirect: 认证失败是否重定向
        :param is_api: 认证失败以JSON返回未认证错误信息
        :return: 认证是否成功
        """
        if self.is_super():
            return True
        points = self.session("bo_caps")
        if not points:
            points = set()
            roles = metas.get_user_metas(self.get_current_user(), "bo_roles")
            all_roles = options.get_options("bo_roles")
            for role in all_roles:
                if role in roles:
                    points |= set(all_roles[role][1])
            self.session("bo_caps", points)
        if caps.issubset(points):
            return True
        else:
            if is_redirect:
                self.redirect(self.get_login_url())
            elif is_api:
                self.render_json("403", "UnAuth", False)
            else:
                return False

    def get_login_url(self):
        return self.reverse_url("login")

    def get_current_user(self):
        """
        user id, int
        :return: user id, int type
        """
        id_ = self.get_secure_cookie("bo_current_user")
        if id_:
            return int(id_.decode('utf-8'))
        return None

    def get_current_user_nickname(self):
        """
        current account user_login field.
        :return:
        """
        name = self.get_secure_cookie("bo_current_user_nickname")
        if name:
            return name.decode('utf-8')
        return ""

    def is_super(self):
        s = self.session("is_bo_super")
        if s:
            return s
        else:
            s = metas.get_user_metas(self.get_current_user(), "bo_super")
            self.session("is_bo_super", s)
        return s

    def session(self, name, value=None):
        """
        get or set session value.
        get: self.session('name')
        set: self.session('name', v)
        :param name:
        :param value:
        :return:
        """
        if not self.current_user:
            return None
        if value:
            Cache.default().set(["session", "%i" % self.current_user, name], value, SESSION_TIMEOUT)
        else:
            return Cache.default().get(["session", "%i" % self.current_user, name])

    def clear_session(self):
        """
        clear session
        """
        if self.current_user:
            Cache.default().delete(["session", "%i" % self.current_user])

    def data_received(self, chunk):
        pass


class ApiHandler(BaseHandler):
    def get(self, action):
        """

        :param action: \w+ 字母、数字、下划线
        """
        do_action("bo_api_get", self, action)

    def post(self, action):
        """

        :param action: \w+ 字母、数字、下划线
        """
        do_action("bo_api_post", self, action)

    def put(self, action):
        """

        :param action: \w+ 字母、数字、下划线
        """
        do_action("bo_api_put", self, action)

    def delete(self, action):
        """

        :param action: \w+ 字母、数字、下划线
        """
        do_action("bo_api_delete", self, action)

    def head(self, action):
        """
        :param action: \w+ 字母、数字、下划线
        """
        do_action("bo_api_head", self, action)

    def options(self, action):
        """
        :param action: \w+ 字母、数字、下划线
        """
        do_action("bo_api_options", self, action)

    def patch(self, action):
        """
        :param action: \w+ 字母、数字、下划线
        """
        do_action("bo_api_patch", self, action)


class AdminHandler(BaseHandler):
    @authenticated
    def get(self):
        do_action("admin_init", self)
        screen_id = self.get_query_argument("page", "")
        if not screen_id:
            screen_id = "bo-dashboard"
        menu_item = AdminMenus.get_menu(screen_id)
        if menu_item:
            caps = menu_item["capability"]
            if self.auth(set(caps), True, False):
                cb = menu_item["cb"]
                if type(cb) is str:
                    self.render(cb, current_menu_item=menu_item, screen_id=screen_id)
                else:
                    cb(self, current_menu_item=menu_item, screen_id=screen_id)
        else:
            self.write("Page Not Found.")


class IndexHandler(BaseHandler):
    def get(self):
        self.redirect(self.get_login_url())


class RegistryHandler(BaseHandler):
    def get(self):
        self.render("admin/register.html")

    def post(self, *args, **kwargs):
        site_options = options.get_site_options()
        if site_options.get("users_can_register") != "Y":
            self.render_json("", success=False, msg="系统已关闭新用户注册")
        else:
            f = UserRegistryFrom(FormData(self))
            if f.validate():
                r = user.registry(f.user_email.data, f.user_pass.data, f.user_email.data)
                if not r.success:
                    self.head_json()
                    self.write(r.to_dict())
                else:
                    u = r.data
                    try:
                        link = "%s%s?ak=%s" % (site_options.get("site_url"),
                                               self.reverse_url("verify"), u.user_activation_key)
                        title = "新用户注册激活"
                        body = """
                        <html><body>点此链接激活账户: <a href="%s">%s</a></body></html>
                        """ % (link, link)
                        Mailer.send_mail([u.user_email], title, body)
                    except Exception as e:
                        Logger.exception(e)
                    self.render_json()
            else:
                self.render_json(f.errors, success=False, msg="")


class LoginHandler(BaseHandler):
    def get(self):
        self.render("admin/login.html")

    def post(self, *args, **kwargs):
        f = UserLoginFrom(FormData(self))
        if f.validate():
            r = user.login(f.user_email.data, f.user_pass.data)
            if r.success:
                u = r.data
                self.set_secure_cookie("bo_current_user", "%i" % u.user_id)
                self.set_secure_cookie("bo_current_user_nickname", u.user_login)
                self.render_json({"next": self.get_query_argument("next", "")})
            else:
                self.write(r.to_dict())
        else:
            self.render_json(f.errors, success=False, msg="")


class LogoutHandler(BaseHandler):
    @authenticated
    def get(self, *args, **kwargs):
        self.clear_session()
        self.clear_cookie("bo_current_user")
        self.clear_cookie("bo_current_user_nickname")
        self.current_user = None
        self.redirect(self.reverse_url("login"))


class VerifyUserHandler(BaseHandler):
    def get(self, *args, **kwargs):
        ak = self.get_query_argument("ak", None)
        if ak:
            s = SessionFactory.session()
            u = s.query(Users).filter(Users.user_activation_key == ak).filter(Users.user_status == 0).one_or_none()
            if u:
                u.user_activation_key = Utils.uniq_index()
                u.user_status = 1
                s.commit()
                self.render("admin/user_verify.html")
            else:
                self.write("账号不存在或已激活，如果忘记密码请使用找回密码功能。")

    def post(self, *args, **kwargs):
        email = self.get_argument("email", None)
        if email:
            s = SessionFactory.session()
            u = s.query(Users).filter(Users.user_email == email).one_or_none()
            if u:
                rnd = Utils.random_str(8)
                try:
                    u.user_pass = Utils.md5(rnd)
                    s.commit()
                    title = "密码找回"
                    body = "新的密码: %s" % rnd
                    Mailer.send_mail([u.user_email], title, body)
                    self.render_json()
                except Exception as e:
                    Logger.error(str(e))
                    self.render_json("", success=False, msg="网络错误")
            else:
                self.render_json("", success=False, msg="找不到使用此邮箱的账户")
        else:
            self.render_json({"fb_user_email": "必须填写"}, success=False, msg="")


urls = [
    url(r'/', IndexHandler),
    url(r'/bopress/admin', AdminHandler, name="admin"),
    url(r'/bopress/api/(\w+)', ApiHandler, name="api"),
    url(r'/bopress/login', LoginHandler, name="login"),
    url(r'/bopress/logout', LogoutHandler, name="logout"),
    url(r'/bopress/user/verify', VerifyUserHandler, name="verify"),
    url(r'/bopress/registry', RegistryHandler, name="registry")
]
